﻿using System;
using System.Linq;
using System.Text;
using IceCS.DataStructure.Core.Stack;
using IceCS.DataStructure.Helpers;

namespace IceCS.DataStructure.TrainSequence
{
    /// <summary>
    /// 火车管理类（顺序栈实现）
    /// </summary>
    public class SequenceTrainManager
    {
        /// <summary>
        /// 入轨
        /// </summary>
        private Carriage[] _inTrack;

        /// <summary>
        /// 入轨车厢数据备份
        /// </summary>
        private Carriage[] _inTrackBackup;

        /// <summary>
        /// 缓冲轨列表
        /// </summary>
        private IStack<Carriage>[] _bufferTrackLists;
        /// <summary>
        /// 出轨
        /// </summary>
        private Carriage[] _outTrack;

        /// <summary>
        /// 入轨车厢长度
        /// </summary>
        private int _inTrackLength;

        /// <summary>
        /// 缓冲轨道数量
        /// </summary>
        private int _bufferTrackCount;

        /// <summary>
        /// 编号最小的车厢所在的缓冲轨编号
        /// </summary>
        private int _minBufferTrack;

        /// <summary>
        /// 在缓冲轨上编号最小的车厢
        /// </summary>
        private int _minCarriageInBufferTrack;

        /// <summary>
        /// 下次输出的车厢编号
        /// </summary>
        private int _nextOut;

        /// <summary>
        /// 输出火车信息
        /// </summary>
        public bool ShowTrainInfo { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="inTrack">入栈车厢列表</param>
        /// <param name="bufferTrackCount">缓冲轨数量</param>
        public SequenceTrainManager(Carriage[] inTrack, int bufferTrackCount)
        {
            _inTrackLength = inTrack.Length;
            _bufferTrackCount = bufferTrackCount;
            _inTrack = inTrack;
            _inTrackBackup = new Carriage[_inTrackLength];
            _inTrack.CopyTo(_inTrackBackup, 0);
            _outTrack = new Carriage[_inTrackLength];
            _bufferTrackLists = new SequenceStack<Carriage>[bufferTrackCount];
            for (int i = 0; i < bufferTrackCount; i++)
            {
                _bufferTrackLists[i] = new SequenceStack<Carriage>(_inTrackLength);
            }
            _minBufferTrack = 0;
            _minCarriageInBufferTrack = _inTrackLength + 1;
            _nextOut = inTrack.Min(c => c.Id);
            ShowTrainInfo = false;
        }

        /// <summary>
        /// 运行
        /// </summary>
        public void Run()
        {
            MainLogic();
        }


        /// <summary>
        /// 主逻辑
        /// </summary>
        private void MainLogic()
        {
            ShowTrainInfoAction();
            bool result = RailRoad();
            do
            {
                if (!result)
                {
                    Console.Clear();
                    var tip = $"需要更多的缓冲轨，请输入额外添加的缓冲轨道（当前是{_bufferTrackCount}）:";
                    var bufferTrackCount = _bufferTrackCount + ConsoleHelper.GetRightInputNumberGreatThanMinValue(tip, 1);
                    UpdateBufferTrackLists(bufferTrackCount);
                    result = RailRoad();
                }
            } while (!result);
            ShowTrainInfoAction();
        }

        /// <summary>
        /// 更新缓冲轨数量
        /// </summary>
        /// <param name="bufferTrackCount"></param>
        private void UpdateBufferTrackLists(int bufferTrackCount)
        {
            _bufferTrackCount = bufferTrackCount;
            _inTrackBackup.CopyTo(_inTrack, 0);
            _outTrack = new Carriage[_inTrackLength];
            _bufferTrackLists = new SequenceStack<Carriage>[bufferTrackCount];
            for (int i = 0; i < bufferTrackCount; i++)
            {
                _bufferTrackLists[i] = new SequenceStack<Carriage>(_inTrackLength);
            }
            _minBufferTrack = 0;
            _minCarriageInBufferTrack = _inTrackLength + 1;
            _nextOut = _inTrack.Min(c => c.Id);
        }

        /// <summary>
        /// 车厢重排算法
        /// </summary>
        /// <returns></returns>
        private bool RailRoad()
        {
            for (int i = 0; i < _inTrackLength; i++)
            {
                //从入轨移出来的车厢有两个选择
                //1.移动到出轨
                //2.移动到缓冲轨
                var carriage = MoveOutFromInTrack(_inTrack[i].Id);
                var carriageId = carriage.Id;

                //如果入轨取出的车厢等于下一个入轨的，直接移入出轨
                if (carriageId == _nextOut)
                {
                    MoveCarriageToOutTrack(carriage);
                    Console.WriteLine($"把【车厢 {carriageId}】从【入轨】移动到【出轨】");
                    ShowTrainInfoAction();
                    _nextOut++;

                    //从缓冲铁轨中输出
                    while (_minCarriageInBufferTrack == _nextOut)
                    {
                        Output();
                        _nextOut++;
                    }
                }
                else
                {
                    //如果车厢放入缓冲轨失败，则排序失败，
                    //表示需要更多的缓冲轨
                    if (!Hold(carriage))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 把【缓冲铁轨】上的【车厢】移动到【出轨】处
        /// </summary>
        private void Output()
        {
            Carriage carriage = _bufferTrackLists[_minBufferTrack].Pop();
            MoveCarriageToOutTrack(carriage);
            Console.WriteLine($"从【缓冲轨{_minBufferTrack + 1}】把【车厢{_minCarriageInBufferTrack}】移动到【出轨】");
            ShowTrainInfoAction();

            //通过检查所有的栈顶，搜索新的缓冲栈上的最下编号的车厢
            _minCarriageInBufferTrack = _inTrackLength + 1;
            for (int i = 0; i < _bufferTrackCount; i++)
            {
                var topCarriage = _bufferTrackLists[i].GetTop();
                if (!_bufferTrackLists[i].IsEmpty && topCarriage.Id < _minCarriageInBufferTrack)
                {
                    _minCarriageInBufferTrack = topCarriage.Id;
                    _minBufferTrack = i;
                }
            }
        }

        /// <summary>
        /// 在一个【缓冲轨】道中放入一个【车厢】
        /// </summary>
        /// <param name="carriage"></param>
        /// <returns></returns>
        private bool Hold(Carriage carriage)
        {
            //如果没有可用的缓冲铁轨，则返回 false


            int carriageId = carriage.Id;
            //目前最优铁轨
            int bestTrack = -1;
            //最优铁轨上的头辆车厢
            int bestTop = _inTrackLength + 1;

            for (int i = 0; i < _bufferTrackCount; i++)
            {
                if (_bufferTrackLists[i].IsEmpty)
                {
                    //缓冲轨 i 为空
                    if (bestTrack == -1)
                    {
                        bestTrack = i;
                    }
                    break;
                }
                else
                {
                    //车厢索引
                    //铁轨 i 不为空
                    int topCarriageId = _bufferTrackLists[i].GetTop().Id;
                    //1.保证放入的车厢id比顶部车厢的id小
                    //2.保证顶部车厢的id是所有缓冲栈中最小
                    if (carriageId < topCarriageId && topCarriageId < bestTop)
                    {
                        //铁轨i顶部的车厢编号最小
                        bestTop = topCarriageId;
                        bestTrack = i;
                    }
                }

            }
            if (bestTrack == -1)
            {
                //没有可用铁轨
                return false;
            }

            //把车厢送入缓冲铁轨
            _bufferTrackLists[bestTrack].Push(carriage);
            Console.WriteLine($"从【入轨】移动【车厢{carriageId}】到【缓冲轨{bestTrack + 1}】");
            ShowTrainInfoAction();

            if (carriageId < _minCarriageInBufferTrack)
            {
                _minCarriageInBufferTrack = carriageId;
                _minBufferTrack = bestTrack;
            }
            return true;
        }

        /// <summary>
        /// 显示火车轨道信息
        /// </summary>
        private void ShowTrainInfoAction()
        {
            if (!this.ShowTrainInfo)
            {
                return;
            }
            Func<Carriage, string> func = c =>
             {
                 if (c == null)
                 {
                     return "空";
                 }
                 else
                 {
                     return c.Id.ToString();
                 }
             };
            var sb = new StringBuilder(100);
            var separator = "------------------------";
            sb.AppendLine(separator);
            sb.AppendLine("入轨的火车车厢顺序(从右到左读)：");
            var trackInfo = string.Join(",", _inTrack.Select(func).Reverse());
            sb.AppendLine(trackInfo);
            sb.AppendLine();
            for (int i = 0; i < _bufferTrackLists.Length; i++)
            {
                var bufferTrack = _bufferTrackLists[i];
                sb.AppendLine($"缓冲轨{i + 1}的车厢信息(从上到下读)：");
                for (int j = bufferTrack.Count - 1; j >= 0; j--)
                {
                    var carriage = bufferTrack[j];
                    sb.AppendLine($"车厢 {carriage.Id}");
                }
                sb.AppendLine();
            }
            sb.AppendLine();
            sb.AppendLine("出轨的火车车厢顺序(从右到左读)：");
            trackInfo = string.Join(",", _outTrack.Select(func).Reverse());
            sb.AppendLine(trackInfo);
            sb.AppendLine();
            sb.AppendLine(separator);

            var text = sb.ToString();
            Console.WriteLine(text);
        }

        /// <summary>
        /// 把车厢从入轨移出
        /// </summary>
        /// <param name="id"></param>
        private Carriage MoveOutFromInTrack(int id)
        {
            for (int i = 0; i < _inTrackLength; i++)
            {
                Carriage carriage = _inTrack[i];
                if (carriage != null && carriage.Id == id)
                {
                    _inTrack[i] = null;
                    return carriage;
                }
            }
            return null;
        }

        /// <summary>
        /// 移动车厢到出轨
        /// </summary>
        /// <param name="carriage"></param>
        private void MoveCarriageToOutTrack(Carriage carriage)
        {
            for (int i = 0; i < _outTrack.Length; i++)
            {
                Carriage currentCarriage = _outTrack[i];
                if (currentCarriage == null)
                {
                    _outTrack[i] = carriage;
                    break;
                }
            }
        }
    }
}
